package com.googlecode.economini.converter;

import java.util.Map;
import java.util.Set;

import org.xml.sax.Attributes;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

public class AttributeMatcher {

    private final boolean noMatchResult;
    private Map<String, Predicate<String>> rules;

    private AttributeMatcher(
            boolean noMatchResult,
            Map<String, Predicate<String>> rules) {
        this.noMatchResult = noMatchResult;
        this.rules = rules;
    }

    public boolean doesMatch(Attributes attributes) {
        Set<String> matchedRules = Sets.newHashSet();
        for (int i = 0; i < attributes.getLength(); i++) {
            String name = attributes.getLocalName(i);
            if (Strings.isNullOrEmpty(name)) {
                name = attributes.getQName(i);
            }
            String value = attributes.getValue(i);

            /* Look up the rule for the attribute */
            Predicate<String> rule = rules.get(name);

            if (rule != null) {
                /* If attribute does not pass rule, return false */
                if (!rule.apply(value)) {
                    return false;
                }

                /* track the rules that were matched */
                matchedRules.add(name);
            } else if (!noMatchResult) {
                /* If the output on a missed match is false, return it here */
                return noMatchResult;
            }
        }

        /* Return result if some rules were not matched */
        if (!matchedRules.equals(rules.keySet())) {
            return noMatchResult;
        }

        return true;
    }

    public static AttributeMatcher any() {
        return new AttributeMatcher(
                true,
                ImmutableMap.<String, Predicate<String>>of());
    }

    public static AttributeMatcher none() {
        return new AttributeMatcher(
                false,
                ImmutableMap.<String, Predicate<String>>of());
    }

    public static class Builder {
        private boolean noMatchResult;
        private Map<String, Predicate<String>> rules;

        public Builder() {
            noMatchResult = false;
            rules = Maps.newHashMap();
        }

        public Builder withAttribute(String name, String value) {
            rules.put(name, Predicates.equalTo(value));
            return this;
        }

        public Builder matchesAll() {
            noMatchResult = true;
            return this;
        }

        public AttributeMatcher build() {
            return new AttributeMatcher(
                    noMatchResult,
                    ImmutableMap.copyOf(rules));
        }
    }
}
